@file:Suppress("unused", "MemberVisibilityCanBePrivate")

package com.hua.utils

import org.apache.commons.text.similarity.CosineDistance
import org.apache.commons.text.similarity.JaccardSimilarity
import java.io.BufferedOutputStream
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.FileReader
import java.io.IOException
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import kotlin.io.path.Path

const val WORKPLACE = "workplace\\"
const val REFERENCES = "references\\"
const val REFERENCES_DIR = "references\\wifimain\\"
const val OUTPUT = "output\\"

//匹配string内容
const val STRING_CONTENT_REGEX = "(?<=>).*(?=</string>)"

//匹配stringKey
const val STRING_KEY_REGEX = "(?<=name\\=\\\").*(?=\\\")"

//起始tag
const val STRING_START_TAG_REGEX = "<string.*\"\\s?>"

//结束tag
const val STRING_END_TAG_REGEX = "</.*\\s?>.*"

//注释行
const val COMMENT = "//.*|<!--.*"

//中文
const val CHINESE = "[\\u4e00-\\u9fa5]"

//表格行tag
const val EXCEL_ROW = "<Row.*"

//单元格tag
const val EXCEL_CELL = "<Cell.*\">|<Cell.*/>"
const val EXCEL_CELL_END = "</.*"
const val EXCEL_CELL_CONTENT = "(?<=String\">).*(?=</Data>)"
const val EXCEL_CELL_EMPTY_COLOR = "#FFF9B8"
const val EXCEL_CELL_BORDER_COLOR = "#A9D08E"
const val EXCEL_CELL_FONT_COLOR = "#000000"


fun main() {
    checkDir()
    filterContentWifi()
//    convertExcelXml("${REFERENCES}source_string.xml")
//    filterContent()
//    convertStringXmlToContentTxt("${REFERENCES}strings_tenda_wifi_zh.xml")
//    fileCompare()
//    output()
}

/**
 * 过滤指定内容
 */
fun filterContent() {
    FileUtil().apply {
        File("${REFERENCES}strings_en_in_zh.xml").scanFiles(operation = {
            it.readAsHashMap(STRING_CONTENT_REGEX, STRING_KEY_REGEX).apply {
                val keyRemove = mutableListOf<String>()
                forEach { (key, _) ->
                    if (Regex(CHINESE).find(key) != null) {
                        keyRemove.add(key)
                    }
                }
                keyRemove.forEach { key ->
                    remove(key)
                }
                val s = StringBuilder()
                val keyBuilder = StringBuilder()
                val valueBuilder = StringBuilder()
                s.append("<resources>\r\n")
                for ((key, value) in entries) {
                    keyBuilder.append("$value\r\n")
                    valueBuilder.append("$key\r\n")
                    if (key != "") s.append("      ${if (value == "") "<!--TODO Need Key-->" else ""}<string name=\"$value\">$key</string>\r\n")
                }
                s.append("</resources>")
                it.saveAs(path = OUTPUT, content = s.toString(), suf = "xml")
                keyBuilder.toString().saveAs(OUTPUT, "key", ".txt")
                valueBuilder.toString().saveAs(OUTPUT, "value", ".txt")
            }
        })
    }
}

fun filterContentWifi() {
    FileUtil().apply {
        File("${REFERENCES_DIR}strings.xml").read { line, builder ->
            if (line.contains("tools:ignore=\"MissingTranslation\"")){
                builder.append(line.replace("\" tools:ignore=\"MissingTranslation", "") + "\r\n")
            }
        }.saveAs(WORKPLACE,"strings_loss",".xml")
//        File(REFERENCES_DIR).scanFiles(operation = {
//            doReplace(it,LinkedHashMap<String, String>().apply {
//                put(COMMENT,"")
//            },true, save = { str ->
//                str.saveAs(REFERENCES_DIR,it.name.replace(it.sufName,""),"xml")
//            })
//        })
        val file = File("${WORKPLACE}strings_loss.xml")
        val strings = file.readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsCs = File("${REFERENCES_DIR}strings_cs.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsDe = File("${REFERENCES_DIR}strings_de.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsEs = File("${REFERENCES_DIR}strings_es.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsFr = File("${REFERENCES_DIR}strings_fr.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsHu = File("${REFERENCES_DIR}strings_hu.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsIn = File("${REFERENCES_DIR}strings_in.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsIt = File("${REFERENCES_DIR}strings_it.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsKo = File("${REFERENCES_DIR}strings_ko.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsPl = File("${REFERENCES_DIR}strings_pl.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsPt = File("${REFERENCES_DIR}strings_pt.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsRo = File("${REFERENCES_DIR}strings_ro.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsRu = File("${REFERENCES_DIR}strings_ru.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsTr = File("${REFERENCES_DIR}strings_tr.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsUk = File("${REFERENCES_DIR}strings_uk.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsVi = File("${REFERENCES_DIR}strings_vi.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsZhRcn = File("${REFERENCES_DIR}strings_zh_rcn.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsZhRhk = File("${REFERENCES_DIR}strings_zh_rhk.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsZhRtw = File("${REFERENCES_DIR}strings_zh_rtw.xml").readAsHashMap(STRING_KEY_REGEX,STRING_CONTENT_REGEX)
        val stringsMap = LinkedHashMap<String,LinkedHashMap<String,String>>().apply {
            put("英语(默认)",strings)
            put("中文",stringsZhRcn)
            put("中国香港",stringsZhRhk)
            put("中国台湾",stringsZhRtw)
            put("捷克语",stringsCs)
            put("法语",stringsFr)
            put("德语",stringsDe)
            put("匈牙利语",stringsHu)
            put("印尼语",stringsIn)
            put("意大利语",stringsIt)
            put("韩语",stringsKo)
            put("波兰语",stringsPl)
            put("巴西葡萄牙语",stringsPt)
            put("罗马尼亚语",stringsRo)
            put("俄语",stringsRu)
            put("西班牙语",stringsEs)
            put("土耳其语",stringsTr)
            put("乌克兰语",stringsUk)
            put("越南语",stringsVi)
        }
        val keyBuilder = StringBuilder()
        val content = StringBuilder()
        val excelXml = StringBuilder()
        val fileLength = file.length()
        excelXml.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
                "<?mso-application progid=\"Excel.Sheet\"?>\n" +
                "<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\" " +
                "xmlns:o=\"urn:schemas-microsoft-com:office:office\" " +
                "xmlns:x=\"urn:schemas-microsoft-com:office:excel\" " +
                "xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\" " +
                "xmlns:html=\"http://www.w3.org/TR/REC-html40\" " +
                "xmlns:dt=\"uuid:C2F41010-65B3-11d1-A29F-00AA00C14882\">" +
                "<DocumentProperties xmlns=\"urn:schemas-microsoft-com:office:office\">" +
                "<Author>FileUtil</Author>" +
                "<LastAuthor>FileUtil</LastAuthor>" +
                "<Created>2023-10-28T03:36:00Z</Created>" +
                "<LastSaved>2023-10-28T03:39:05Z</LastSaved>" +
                "</DocumentProperties>" +
                "<CustomDocumentProperties xmlns=\"urn:schemas-microsoft-com:office:office\">" +
                "<ICV dt:dt=\"string\">1B9376AEE6014BDC8B2C635D59B6E738_13</ICV>" +
                "<KSOProductBuildVer dt:dt=\"string\">2052-12.1.0.15398</KSOProductBuildVer>" +
                "</CustomDocumentProperties>" +
                "<ExcelWorkbook xmlns=\"urn:schemas-microsoft-com:office:excel\">" +
                "<WindowWidth>27945</WindowWidth><WindowHeight>12255</WindowHeight>" +
                "<ProtectStructure>False</ProtectStructure>" +
                "<ProtectWindows>False</ProtectWindows>" +
                "</ExcelWorkbook>" +
                "<Styles>" +
                "<Style ss:ID=\"Default\" ss:Name=\"Normal\">" +
                "<Alignment ss:Vertical=\"Center\"/>" +
                "<Font ss:FontName=\"微软雅黑\" x:CharSet=\"134\" ss:Size=\"11\" ss:Color=\"#000000\"/>" +
                "</Style>" +
                "<Style ss:ID=\"s49\">" +
                "<Alignment ss:Horizontal=\"Center\" ss:Vertical=\"Center\"/>" +
                "<Font ss:FontName=\"微软雅黑\" x:CharSet=\"134\" ss:Size=\"11\" ss:Color=\"#FFFFFF\"/>" +
                "<Interior ss:Color=\"#00B050\" ss:Pattern=\"Solid\"/>" +
                "</Style>" +
                "<Style ss:ID=\"s50\">" +
                "<Alignment ss:Vertical=\"Center\" ss:WrapText=\"1\"/>" +
                "<Font ss:FontName=\"微软雅黑\" x:CharSet=\"134\" ss:Size=\"11\" ss:Color=\"$EXCEL_CELL_FONT_COLOR\"/>" +
                "<Interior/>" +
                "</Style>" +
                "<Style ss:ID=\"s51\">" +
                "<Alignment ss:Vertical=\"Center\" ss:WrapText=\"1\"/>" +
                "<Font ss:FontName=\"微软雅黑\" x:CharSet=\"134\" ss:Size=\"11\" ss:Color=\"#000000\"/>" +
                "<Interior ss:Color=\"${EXCEL_CELL_EMPTY_COLOR}\" ss:Pattern=\"Solid\"/>" +
                "<Borders>" +
                "<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\" ss:Color=\"$EXCEL_CELL_BORDER_COLOR\"/>" +
                "<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\" ss:Color=\"$EXCEL_CELL_BORDER_COLOR\"/>" +
                "<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\" ss:Color=\"$EXCEL_CELL_BORDER_COLOR\"/>" +
                "<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\" ss:Color=\"$EXCEL_CELL_BORDER_COLOR\"/>" +
                "</Borders>"+
                "</Style>" +
                "</Styles>" +
                "<Worksheet ss:Name=\"Sheet1\">" +
                "<Table " +
                "ss:ExpandedColumnCount=\"10\" " +
                "ss:ExpandedRowCount=\"2632\" " +
                "x:FullColumns=\"1\" " +
                "x:FullRows=\"1\" " +
                "ss:DefaultColumnWidth=\"100\" " +
                "ss:DefaultRowHeight=\"30\">" +
                "<Column " +
                "ss:Index=\"1\" ss:StyleID=\"s50\" " +
                "ss:AutoFitWidth=\"0\" " +
                "ss:Width=\"100\" " +
                "ss:Span=\"0\"/>\r\n")
        //表头
        excelXml.append("<Row ss:Height=\"30\">\n")
        excelXml.append("<Cell ss:StyleID=\"s49\"><Data ss:Type=\"String\">Key</Data></Cell>\r\n")
        stringsMap.forEach { (t, _) ->
            excelXml.append("<Cell ss:StyleID=\"s49\"><Data ss:Type=\"String\">$t</Data></Cell>\r\n")
        }
        excelXml.append("<Cell ss:StyleID=\"s49\"><Data ss:Type=\"String\">缺失语言</Data></Cell>\r\n")
        excelXml.append("</Row>\r\n")
        for ((key, value) in strings.entries) {
            keyBuilder.append(key + "\r\n")
            content.append(value + "\r\n")
            val lossLanguage = StringBuilder()
            //内容
            excelXml.append("<Row ss:Height=\"30\">\r\n")
            excelXml.append(
                "<Cell ss:StyleID=\"s50\"><Data ss:Type=\"String\">$key</Data></Cell>\r\n"
            )
            stringsMap.forEach { (lan, map) ->
                if (map.containsKey(key) && map[key]!!.isNotEmpty()) excelXml.append(
                    "<Cell ss:StyleID=\"s50\"><Data ss:Type=\"String\">${map[key]}</Data></Cell>\r\n"
                )else {
                    excelXml.append("<Cell ss:StyleID=\"s51\"/>")
                    lossLanguage.append("$lan,")
                }
            }
            excelXml.append(
                "<Cell ss:StyleID=\"s50\"><Data ss:Type=\"String\">$lossLanguage</Data></Cell>\r\n"
            )
            excelXml.append("</Row>\r\n")
            println(
                "generate xml: Total:$fileLength/${excelXml.length} "
            )
        }
        excelXml.append("</Table>" +
                "<WorksheetOptions " +
                "xmlns=\"urn:schemas-microsoft-com:office:excel\">" +
                "<PageSetup>" +
                "<Header/>" +
                "<Footer/>" +
                "</PageSetup>" +
                "<Selected/>" +
                "<TopRowVisible>2</TopRowVisible>" +
                "<LeftColumnVisible>0</LeftColumnVisible>" +
                "<PageBreakZoom>100</PageBreakZoom>" +
                "<Panes>" +
                "<Pane>" +
                "<Number>3</Number>" +
                "<ActiveRow>8</ActiveRow>" +
                "<ActiveCol>7</ActiveCol>" +
                "<RangeSelection>R9C8</RangeSelection>" +
                "</Pane>" +
                "</Panes>" +
                "<ProtectObjects>False</ProtectObjects>" +
                "<ProtectScenarios>False</ProtectScenarios>" +
                "</WorksheetOptions>" +
                "</Worksheet>" +
                "</Workbook>")
        keyBuilder.toString().saveAs(OUTPUT,"keys",".txt")
        content.toString().saveAs(OUTPUT,"contents",".txt")
        excelXml.toString().saveAs(OUTPUT,"excel_strings",".xml")
    }
}


/**
 * 提取AndroidString文件中所有的内容分行显示txt
 */
fun convertStringXmlToContentTxt(url: String) {
    replaceContent(
        urls = mutableListOf(url),
        replaceMapping = {
            it.apply {
//                put("content","contentReplaced")
                put("<resources>", "")
                put("</resources>", "")
                put(STRING_START_TAG_REGEX, "")
                put(STRING_END_TAG_REGEX, "")
                put(COMMENT, "")
            }
        }
    )
}

/**
 * 整理表格导出的xml文件
 */
fun convertExcelXml(urls: String) {
    FileUtil().apply {
        var filename = ""
        //Excel默认输出单行xml，预处理转换成多行
        val file = File(urls)
        doReplace(
            file = file,
            replaceMapping = LinkedHashMap<String, String>().apply {
                put("<Row", "\r\n<Row")
                put("<Cell", "\r\n   <Cell")
            },
            true,
            save = {
                filename = "${file.name.replace(".${file.sufName}", "")}_converted"
                it.saveAs(REFERENCES, filename, ".xml")
            }
        )
        val convertedFile = File("${REFERENCES}$filename.xml")
        val sourceMapEn = File("${REFERENCES}strings.xml").readAsHashMap(
            STRING_CONTENT_REGEX, STRING_KEY_REGEX
        )
        val sourceMapZh = File("${REFERENCES}strings_zh.xml").readAsHashMap(
            STRING_CONTENT_REGEX, STRING_KEY_REGEX
        )

        filename = "${convertedFile.name.replace(".${convertedFile.sufName}", "")}_replaced"
        var lastIndex = 0
        val fileLength = convertedFile.length()
        var inserted = false
        var lineNum = 0
        var lineNoKey = 0
        convertedFile.read { line, builder ->
            lineNum++
            builder.append(line + "\r\n")
            if (Regex(EXCEL_ROW).matches(line)) {
                lastIndex = builder.length - 1
                inserted = false
            } else if (lastIndex != 0 && !inserted) {
                val content = Regex(EXCEL_CELL_CONTENT).find(line)?.value
                var key = ""
                if (content != null) {
                    sourceMapZh.getMostSimilarKey(content, 0.1)?.let {
                        key = sourceMapZh[it] ?: ""
                    }
                    if (key.isEmpty()) sourceMapEn.getMostSimilarKey(content,0.1)?.let {
                        key = sourceMapEn[it] ?: ""
                    }
                }
                builder.insert(lastIndex,"   <Cell ss:StyleID=\"s50\"><Data ss:Type=\"String\">$key</Data></Cell>")
                if (key.isEmpty()) lineNoKey++
                inserted = true
            }
            println(
                "find keys in sources: "
                        + "${((builder.length * 100)/fileLength).toDouble()}% "
                        + "Total:$fileLength/${builder.length} "
                        + "lines:$lineNum noKeyLines:$lineNoKey"
//                + "\r\ncurrentLine: $line"
            )
        }.saveAs(WORKPLACE, filename, ".xml")

    }
}

/**
 * 检查是否具有必要操作路径
 */
fun checkDir() {
    if (!File(REFERENCES).exists()) Files.createDirectory(Path(REFERENCES))
    if (!File(WORKPLACE).exists()) Files.createDirectory(Path(WORKPLACE))
    if (!File(OUTPUT).exists()) Files.createDirectory(Path(OUTPUT))
}

/**
 * 替换内容
 */
fun replaceContent(
    urls: MutableList<String> = mutableListOf(),
    replaceMapping: (map: LinkedHashMap<String, String>) -> Unit,
    utilField: ((util: FileUtil) -> Unit)? = null
) {
    FileUtil().apply {
        File(WORKPLACE).clear()
        urls.forEach {
            File(it).scanFiles(
                operation = { file: File ->
                    doReplace(
                        file, LinkedHashMap<String, String>().apply {
                            replaceMapping(this)
                        }, true,
                        save = { str ->
                            str.saveAs(
                                WORKPLACE,
                                "${file.name.replace(".${file.sufName}", "")}_replaced",
                                ".txt"
                            )
                        }
                    )
                },
                fileFilter = this::defaultFilter
            )
        }
        utilField?.invoke(this)
    }
}

/**
 * 过滤指定内容，输出差异内容
 */
fun fileCompare() {
    replaceContent(
        urls = mutableListOf(
            "${REFERENCES}strings.xml",
            "${REFERENCES}entries.txt"
        ),
        replaceMapping = {
            it.apply {
//                put("content","contentReplaced")
                put("<resources>", "")
                put("</resources>", "")
                put(STRING_START_TAG_REGEX, "")
                put(STRING_END_TAG_REGEX, "")
                put(COMMENT, "")
            }
        },
        utilField = {
            it.apply {
                //现有词条
                val curSet = File("${WORKPLACE}strings_replaced.txt").readAsSet()
                //提供的词条表
                val newSet = File("${WORKPLACE}entries_replaced.txt").readAsSet()
                //合集
                val all = LinkedHashSet<String>(curSet).apply { union(newSet) }
                all.saveAs(WORKPLACE, "all", ".txt")
                //交集
                val retain = LinkedHashSet<String>(curSet).apply { retainAll(newSet) }
                retain.saveAs(WORKPLACE, "retain", ".txt")
                //交集
                val intersection = LinkedHashSet<String>(curSet).apply { intersect(newSet) }
                intersection.saveAs(WORKPLACE, "intersection", ".txt")
                //差集
                val differCur = LinkedHashSet<String>(curSet).apply { removeAll(newSet) }
                differCur.saveAs(WORKPLACE, "differCur", ".txt")
                //差集
                val differNew = LinkedHashSet<String>(newSet).apply { removeAll(curSet) }
                differNew.saveAs(WORKPLACE, "differNew", ".txt")
            }
        }
    )
}

/**
 * 输出
 */
fun output() {
    FileUtil().apply {
        File(OUTPUT).clear()
        //这里将key、value反着存是为了生成文件的时候好自动填充key
        val sourceMap = File("${REFERENCES}strings.xml").readAsHashMap(STRING_CONTENT_REGEX, STRING_KEY_REGEX)
        val fileMapList = mutableMapOf<File, LinkedHashMap<String, String>>()
        File(WORKPLACE).scanFiles(
            operation = {
                fileMapList[it] = it.readAsHashMap(".*", "")
            },
            fileFilter = { false }
        )
        for ((key, value) in sourceMap) {
            for ((_, map) in fileMapList) {
                if (map.containsKey(key)) map[key] = value
                else map.getMostSimilarKey(key, 0.05)?.let { map[it] = value }
            }
        }
        for ((file, map) in fileMapList) {
            val s = StringBuilder()
            s.append("<resources>\r\n")
            for ((key, value) in map.entries) {
                if (key != "") s.append("      ${if (value == "") "<!--TODO Need Key-->" else ""}<string name=\"$value\">$key</string>\r\n")
            }
            s.append("</resources>")
            file.saveAs(path = OUTPUT, content = s.toString(), suf = "xml")
        }

    }
}

/**
 * 字符串匹配
 */
fun String.isAnyOf(vararg str: String): Boolean {
    return str.any { it.equals(this, true) }
}

/**
 * 字符串相似度
 */
fun String.similarityOf(str: String): Double {
    return if (this.isNotEmpty() && str.isNotEmpty()) kotlin.math.min(
        JaccardSimilarity().apply(this, str),
        CosineDistance().apply(this, str)
    ) else 0.0
}

/**
 * 获取map中与目标key字串最相似的key
 * @param target 目标字串
 * @param tolerance 容差，范围0-1值越大越粗略，反之越精确
 */
fun LinkedHashMap<String, String>.getMostSimilarKey(target: String, tolerance: Double = 1.0): String? {
    var result: String? = null
    var similarity = 0.0
    for ((key, _) in this) {
        val sim = key.similarityOf(target)
        if (sim > (1.0 - tolerance) && sim > similarity) {
            similarity = sim
            result = key
        }
    }
    return result
}

class FileUtil {

    /**
     * 替换操作
     * @param file 文件
     */
    fun doReplace(
        file: File,
        replaceMapping: LinkedHashMap<String, String>,
        isRegex: Boolean,
        save: (s: String) -> Unit = { file.save(it) },
    ) {
        var s = file.read()
        for ((key, value) in replaceMapping.entries) {
            //替换为替换的内容
            s = if (isRegex) Regex(key).replace(s, value)
            else s.replace(key, value)
        }
        save(s)
    }

    /**
     * 扫描路径下的所有文件
     */
    fun File.scanFiles(operation: (file: File) -> Unit, fileFilter: (file: File) -> Boolean = { false }) {
        if (!exists()) {
            println("$absoluteFile Don't exist!!!")
            return
        }
        if (isDirectory) {
            val fileList = listFiles()
            if (fileList.isNullOrEmpty()) {
                println("$absoluteFile Dir is Empty!!!")
                return
            }
            for (file in fileList) file.scanFiles(operation, fileFilter)
        } else if (isFile && !fileFilter(this)) operation(this)
    }

    fun defaultFilter(file: File): Boolean {
        return file.sufName.isAnyOf("java", "class", "kt", "bat")
    }

    /**
     * 读取文件后缀名
     * @return String
     */
    val File.sufName get() = name.substring(name.lastIndexOf(".") + 1)

    /**
     * 从文件中读取数据到一个集合中
     */
    fun File.readAsSet(): LinkedHashSet<String> {
        return try {
            LinkedHashSet<String>().apply {
                BufferedReader(FileReader(this@readAsSet, StandardCharsets.UTF_8)).use { reader ->
                    while (reader.ready()) add(reader.readLine().trim() + "\r\n")
                    reader.close()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            LinkedHashSet()
        }
    }

    /**
     * 从文件中读取数据到一个集合中
     */
    fun File.readAsHashMap(keyRegex: String, valueRegex: String): LinkedHashMap<String, String> {
        return try {
            LinkedHashMap<String, String>().apply {
                BufferedReader(FileReader(this@readAsHashMap, StandardCharsets.UTF_8)).use { reader ->
                    while (reader.ready()) reader.readLine().let {
                        put(
                            Regex(keyRegex).find(it)?.value?.trim() ?: "",
                            Regex(valueRegex).find(it)?.value?.trim() ?: ""
                        )
                    }
                    reader.close()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            LinkedHashMap()
        }
    }

    /**
     * 读取文件
     */
    fun File.read(lines: ((line: String, builder: StringBuilder) -> Unit)? = null): String {
        return try {
            val result = StringBuilder()
            BufferedReader(InputStreamReader(FileInputStream(this), StandardCharsets.UTF_8)).use { reader ->
                while (reader.ready()) {
                    if (lines == null)
                        result.append(reader.readLine().trim() + "\r\n")
                    else lines(reader.readLine(), result)
                }
                reader.close()
            }
            result.toString()
        } catch (e: IOException) {
            e.printStackTrace()
            ""
        }
    }


    /**
     * 保存文件
     */
    fun File.save(s: String) {
        try {
            FileOutputStream(this).use { fos ->
                BufferedOutputStream(fos).use { bos ->
                    bos.write(s.toByteArray(StandardCharsets.UTF_8))
                    println("Save Success:$name")
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
            println("Save Failed:$name")
        }
    }

    /**
     * 保存为
     */
    fun File.saveAs(content: String, suf: String) {
        if (isFile) File(absolutePath.replace(sufName, suf)).save(content)
    }

    fun File.saveAs(path: String, content: String, filename: String = this.name, suf: String) {
        if (isFile) File(path + filename.replace(sufName, suf)).save(content)
    }

    fun File.saveAs(content: String, filename: String, suf: String) {
        if (isFile) File(absolutePath + filename + suf).save(content)
    }

    fun String.saveAs(path: String, filename: String, suf: String) {
        File(path + filename + suf).save(this)
    }

    fun LinkedHashSet<String>.saveAs(path: String, filename: String, suf: String) {
        val s = StringBuilder()
        forEach { s.append(it) }
        File(path + filename + suf).save(s.toString())
    }

    /**
     * 清空文件夹或删除文件
     */
    fun File.clear() {
        when {
            !this.exists() -> println("${this.name} not exists!!!")
            this.isFile -> {
                println("${this.name} has deleted")
                this.delete()
            }
            this.isDirectory -> listFiles()?.let { for (file in it) file.clear() }
        }
    }

}
